using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
namespace eTMS.Services
{
    public class ServiceBase
    {
        #region Variables
        private bool _IsAllowCache = false;
        public bool IsAllowCache
        {
            get { return _IsAllowCache; }
            set
            {
                _IsAllowCache = value;
            }
        }
        #endregion
        #region Caching
        public void AddCache(string key, object data)
        {
            if (_IsAllowCache)
            {
                AddToCache(key, data);
            }
        }
        public void RemoveCache(string key)
        {
            if (_IsAllowCache)
            {
                Cache.Remove(key);
            }
        }
        public static void AddToCache(string key, object data)
        {
            Cache.Set(key, data, CacheTimeOut);
        }
        protected static int CacheTimeOut = 200;
        private static ICacheManager _Cache = new CacheManager();
        public static ICacheManager Cache
        {
            get
            {
                return _Cache;
            }
        }
        public static void InitCache()
        {
            _Cache = new CacheManager();
        }
        #endregion
    }

    /// <summary>
    /// Represents a StaticCache
    /// </summary>
    public class CacheManager : ICacheManager
    {
        private Dictionary<string, object> _context;
        private Dictionary<string, int> timeOut;
        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="context">Context</param>
        public CacheManager()
        {
            this._context = new Dictionary<string, object>();
            timeOut = new Dictionary<string, int>();
        }
        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="key">The key of the value to get.</param>
        /// <returns>The value associated with the specified key.</returns>
        public T Get<T>(string key)
        {
            if (!this._context.ContainsKey(key))
                return default(T);
            return (T)_context[key];
        }

        /// <summary>
        /// Adds the specified key and object to the cache.
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="data">Data</param>
        /// <param name="cacheTime">Cache time</param>
        public void Set(string key, object data, int cacheTime)
        {
            if (this._context.ContainsKey(key))
            {
                this._context[key] = data;
                this.timeOut[key] = cacheTime;
            }
            else
            {
                this._context.Add(key, data);
                this.timeOut.Add(key, cacheTime);
            }
            if (thread == null)
            {
                thread = new Thread(new ThreadStart(CleanCacheThread));
                thread.Start();
            }
        }
        private void CleanCacheThread()
        {
            while (timeOut.Count > 0)
            {
                List<string> lsKeys = new List<string>(timeOut.Keys);
                foreach (string key in lsKeys)
                {
                    timeOut[key] = timeOut[key] - 1;
                    if (timeOut[key] <= 0)
                    {
                        Remove(key);
                    }
                }
                System.Threading.Thread.Sleep(1000);
            }
            thread = null;
        }
        private Thread thread;
        /// <summary>
        /// Gets a value indicating whether the value associated with the specified key is cached
        /// </summary>
        /// <param name="key">key</param>
        /// <returns>Result</returns>
        public bool IsSet(string key)
        {
            if (_context == null)
                return false;
            return (_context[key] != null);
        }

        /// <summary>
        /// Removes the value with the specified key from the cache
        /// </summary>
        /// <param name="key">/key</param>
        public void Remove(string key)
        {
            if (_context == null)
                return;
            _context.Remove(key);
            timeOut.Remove(key);
        }

        /// <summary>
        /// Removes items by pattern
        /// </summary>
        /// <param name="pattern">pattern</param>
        public void RemoveByPattern(string pattern)
        {
            if (_context == null)
                return;
            var enumerator = _context.GetEnumerator();
            var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
            var keysToRemove = new List<String>();
            while (enumerator.MoveNext())
            {
                if (regex.IsMatch(enumerator.Current.Key.ToString()))
                {
                    keysToRemove.Add(enumerator.Current.Key.ToString());
                }
            }

            foreach (string key in keysToRemove)
            {
                Remove(key);
            }
        }

        /// <summary>
        /// Clear all cache data
        /// </summary>
        public void Clear()
        {
            _context.Clear();
            timeOut.Clear();
        }
    }
    public interface ICacheManager
    {

        /// <summary>
        /// Gets or sets the value associated with the specified key.
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="key">The key of the value to get.</param>
        /// <returns>The value associated with the specified key.</returns>
        T Get<T>(string key);

        /// <summary>
        /// Adds the specified key and object to the cache.
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="data">Data</param>
        /// <param name="cacheTime">Cache time</param>
        void Set(string key, object data, int cacheTime);

        /// <summary>
        /// Gets a value indicating whether the value associated with the specified key is cached
        /// </summary>
        /// <param name="key">key</param>
        /// <returns>Result</returns>
        bool IsSet(string key);

        /// <summary>
        /// Removes the value with the specified key from the cache
        /// </summary>
        /// <param name="key">/key</param>
        void Remove(string key);

        /// <summary>
        /// Removes items by pattern
        /// </summary>
        /// <param name="pattern">pattern</param>
        void RemoveByPattern(string pattern);

        /// <summary>
        /// Clear all cache data
        /// </summary>
        void Clear();
    }
}